package com.wyz.calibur.infrastructure.config.security;

import com.wyz.calibur.constant.AuthConstant;
import com.wyz.calibur.enums.RedisKeyEnum;
import com.wyz.calibur.enums.ResultCode;
import com.wyz.calibur.infrastructure.config.common.IgnoreUrlConstant;
import com.wyz.calibur.infrastructure.utils.ContextUtil;
import com.wyz.calibur.infrastructure.utils.RedisUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Iterator;

/**
 * @Description: 访问决策管理器
 * @Author: wei yz
 * @Date: 2022/6/24 10:22
 */
public class CliburAccessDecisionManager implements AccessDecisionManager {
    private static final Logger log = LoggerFactory.getLogger(CliburAccessDecisionManager.class);

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IgnoreUrlConstant ignoreUrlConstant;

    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, AuthenticationException {
        HttpServletRequest request = ((FilterInvocation) o).getRequest();
        String token = ContextUtil.getToken(request);
        log.info("==> 进入访问决策管理器");
        Iterator<ConfigAttribute> iterator = collection.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute ca = iterator.next();
            // 当前请求需要的权限
            String needRole = ca.getAttribute();
            // 查询器中没有此权限菜单，检查是否存在白名单中
            if (AuthConstant.ROLE_LOGIN.equals(needRole) && !CollectionUtils.isEmpty(ignoreUrlConstant.getUrl())) {
                PathMatcher pathMatcher = new AntPathMatcher();
                log.info("==> 校验白名单");
                for (String path : ignoreUrlConstant.getUrl()) {
                    if (pathMatcher.match(path, request.getRequestURI())) {
                        return;
                    }
                }
            }
            // token为空
            if (StringUtils.isBlank(token)) {
                throw new InsufficientAuthenticationException(String.valueOf(ResultCode.PARAMETER_ERROR.getCode()));
            }
            // token失效
            String redisToken = (String) redisUtil.get(RedisKeyEnum.AUTH_TOKEN.getKey() + token);
            if (StringUtils.isBlank(redisToken)) {
                throw new InsufficientAuthenticationException(String.valueOf(ResultCode.UNAUTHORIZED.getCode()));
            }
            // 比较当前用户所具有的权限
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        log.info("==> 没有权限");
        throw new AccessDeniedException("没有访问权限");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
